// import com.alibaba.druid.DbType;
// import com.alibaba.druid.sql.ast.*;
// import com.alibaba.druid.sql.visitor.SQLASTVisitor;

// import java.text.SimpleDateFormat;
// import java.util.Collections;
// import java.util.Date;
// import java.util.List;
// import java.util.TimeZone;

#include "SQLTimeExpr.h"
#include "../SQLDataTypeImpl.h"
#include "../../../DbType.h"
#include "SQLCharExpr.h"
#include "../../../utils/instanceof.h"
#include "../../visitor/SQLASTVisitor.h"

#include <sstream>

SQLDataType_ptr SQLTimeExpr::DATA_TYPE = SQLDataType_ptr(new SQLDataTypeImpl(make_string_ptr("time")));

static std::list<DbType_ptr> tmp = {
    DbType_ptr(new DbType(&DbType::mysql)),
    DbType_ptr(new DbType(&DbType::oracle)),
    DbType_ptr(new DbType(&DbType::presto)),
    DbType_ptr(new DbType(&DbType::trino)),
    DbType_ptr(new DbType(&DbType::postgresql)),
    DbType_ptr(new DbType(&DbType::mariadb)),
    DbType_ptr(new DbType(&DbType::tidb))};

long SQLTimeExpr::supportDbTypes = DbType::of(std::make_shared<std::list<DbType_ptr>>(tmp));

SQLTimeExpr::SQLTimeExpr()
{
}

SQLTimeExpr::SQLTimeExpr(std::chrono::system_clock::time_point now, string_ptr timeZone)
{
  setLiteral(now, timeZone);
}

void SQLTimeExpr::setLiteral(std::chrono::system_clock::time_point x, string_ptr timeZone)
{
  // boost::local_time::time_zone_ptr tz(new boost::local_time::posix_time_zone("CET+1"));
  // boost::posix_time::ptime pt(boost::gregorian::date(2009, 1, 5), boost::posix_time::time_duration(12, 0, 0));
  // boost::local_time::local_date_time dt(x, timeZone);
  // boost::posix_time::ptime pt = dt.local_time();
  // // boost::local_time::time_zone_ptr tz2(new boost::local_time::posix_time_zone("EET+2"));
  // // std::cout << dt.local_time_in(tz2).local_time() << std::endl;
  // string_ptr  formatted_time = boost::posix_time::to_simple_string(pt);
  // std::size_t pos = formatted_time.find(":");
  // string_ptr  hours = formatted_time.substr(0, pos);
  // string_ptr  minutes_seconds = formatted_time.substr(pos + 1);

  // std::size_t pos2 = minutes_seconds.find(":");
  // string_ptr  minutes = minutes_seconds.substr(0, pos2);
  // string_ptr  seconds = minutes_seconds.substr(pos2 + 1);

  // if (x == null) {
  //     this->literal = null;
  //     return;
  // }

  // SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
  // if (timeZone != null) {
  //     format.setTimeZone(timeZone);
  // }
  // string_ptr  text = format.format(x);
  // string_ptr  text = hours + ":" + minutes + ":" + seconds;
  std::time_t now_c = std::chrono::system_clock::to_time_t(x);
  std::stringstream sstream;
  std::string text;
  sstream << std::put_time(std::localtime(&now_c), "%H:%M:%S");
  sstream >> text;
  setLiteral(make_string_ptr(text));
}

SQLTimeExpr::SQLTimeExpr(string_ptr literal)
{
  this->setLiteral(literal);
}

SQLExpr_ptr SQLTimeExpr::getLiteral()
{
  return literal;
}

void SQLTimeExpr::setLiteral(string_ptr literal)
{
  setLiteral(SQLCharExpr_ptr(new SQLCharExpr(literal)));
}

void SQLTimeExpr::setLiteral(SQLExpr_ptr x)
{
  if (x != nullptr)
  {
    x->setParent(SharedObject(SQLTimeExpr));
  }
  this->literal = x;
}

//@Override
SQLDataType_ptr SQLTimeExpr::computeDataType()
{
  return DATA_TYPE;
}

string_ptr SQLTimeExpr::getValue()
{
  if (instanceof <SQLExpr, SQLCharExpr>(literal.get()))
  {
    return (std::dynamic_pointer_cast<SQLCharExpr>(literal))->getText();
  }
  return make_string_ptr("");
}

//@Override
BOOL_ptr SQLTimeExpr::replace(SQLExpr_ptr expr, SQLExpr_ptr target)
{
  if (this->literal == expr)
  {
    setLiteral(target);
    return BOOL::TRUE;
  }
  return BOOL::FALSE;
}

//@Override
void SQLTimeExpr::accept0(SQLASTVisitor_ptr visitor)
{
  visitor->visit(SharedObject(SQLTimeExpr));
  visitor->endVisit(SharedObject(SQLTimeExpr));
}

//@Override
int SQLTimeExpr::hashCode()
{
  int prime = 31;
  int result = 1;
  result = prime * result + ((literal == nullptr) ? 0 : literal->hashCode());
  return result;
}

//@Override
bool SQLTimeExpr::equals(Object_ptr obj)
{
  if (this == obj.get())
  {
    return true;
  }
  if (obj == nullptr)
  {
    return false;
  }
  if (getClass() != obj->getClass())
  {
    return false;
  }
  std::shared_ptr<SQLTimeExpr> other = std::dynamic_pointer_cast<SQLTimeExpr>(obj);
  if (literal == nullptr)
  {
    if (other->literal != nullptr)
    {
      return false;
    }
  }
  else if (!literal->equals(other->literal))
  {
    return false;
  }
  return true;
}

SQLObject_ptr SQLTimeExpr::clone()
{
  SQLObject_ptr clone_tmp = std::shared_ptr<SQLTimeExpr>(new SQLTimeExpr());
  std::shared_ptr<SQLTimeExpr> x = std::dynamic_pointer_cast<SQLTimeExpr>(clone_tmp);
  // SQLTimeExpr x = new SQLTimeExpr();

  if (this->literal != nullptr)
  {
    x->setLiteral(std::dynamic_pointer_cast<SQLExpr>(literal->clone()));
  }

  return x;
}

//@Override
SQLObject_list_ptr SQLTimeExpr::getChildren()
{
  return SQLObject_list_ptr();
}

BOOL_ptr SQLTimeExpr::isSupport(DbType_ptr dbType)
{
  return ((dbType->mask & supportDbTypes) != 0)?BOOL::TRUE:BOOL::FALSE;
}

BOOL_ptr SQLTimeExpr::check(string_ptr str)
{
  if (str->empty() || str->length() != 8)
  {
    return BOOL::FALSE;
  }

  if (str->at(2) != ':' && str->at(5) != ':')
  {
    return BOOL::FALSE;
  }

  char c0 = str->at(0);
  char c1 = str->at(1);
  char c3 = str->at(3);
  char c4 = str->at(4);
  char c6 = str->at(6);
  char c7 = str->at(7);

  if (c0 < '0' || c0 > '9')
  {
    return BOOL::FALSE;
  }
  if (c1 < '0' || c1 > '9')
  {
    return BOOL::FALSE;
  }
  if (c3 < '0' || c3 > '9')
  {
    return BOOL::FALSE;
  }
  if (c4 < '0' || c4 > '9')
  {
    return BOOL::FALSE;
  }
  if (c6 < '0' || c6 > '9')
  {
    return BOOL::FALSE;
  }
  if (c7 < '0' || c7 > '9')
  {
    return BOOL::FALSE;
  }

  int HH = (c0 - '0') * 10 + (c1 - '0');
  int mm = (c3 - '0') * 10 + (c4 - '0');
  int ss = (c6 - '0') * 10 + (c7 - '0');

  if (HH > 24 || mm > 60 || ss > 60)
  {
    return BOOL::FALSE;
  }

  return BOOL::TRUE;
}
